package com.geor.gcv.ict;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.nutz.json.Json;
import org.nutz.lang.Files;
import org.nutz.lang.Lang;
import org.nutz.log.Loggers;
import org.opencv.core.Mat;
import org.slf4j.Logger;

import com.geor.gcv.core.mat.Mats;
import com.geor.gcv.core.process.GrayProcess;
import com.geor.gcv.core.test.Tester;

/**
 * <p>
 * 通用的分类器,汉明距离
 * <p>
 * 碎片必须是二值化，正方形。
 * 
 * @author geor.lusongnan
 *
 */
public class GeneralClassifierImpl implements Classifier {
	static Logger log = Loggers.get();
	public static final int MAXSIZE = 500;
	private Map<String, Set<DataMatrix>> trainData = new HashMap<String, Set<DataMatrix>>();
	Tester tester = new Tester();

	public GeneralClassifierImpl() {
	}

	@Override
	public void train(String in, String out) {
		File[] charDirs = new File(in).listFiles();
		Map<String, Set<DataMatrix>> dataMap = new HashMap<String, Set<DataMatrix>>(charDirs.length);
		for (File charDir : charDirs) {
			log.debug(String.format("ready to learn: %s ...", charDir.getName()));
			Set<DataMatrix> dataSet = new HashSet<DataMatrix>();
			List<File> files = Lang.array2list(charDir.listFiles());
			// 按时间倒序
			Collections.sort(files, new Comparator<File>() {
				@Override
				public int compare(File f1, File f2) {
					return (int) (f2.lastModified() - f1.lastModified());
				}
			});
			for (File file : files) {
				try {
					Mat mat = Mats.imread(file.getAbsolutePath());
					mat = new GrayProcess(mat).process();
					MatMatrix matrix = new MatMatrix(mat);
					int[] data = matrix.getData();
					dataSet.add(new DataMatrix(data));
					if (dataSet.size() >= MAXSIZE)
						break;
				} catch (Exception e) {
					log.error("err", e);
				}
			}
			dataMap.put(charDir.getName(), dataSet);
		}

		// out 是否存在
		File outFile = new File(out);
		byte[] data = null;
		// 合并
		if (outFile.exists()) {
			// 读取
			Map<String, Set<DataMatrix>> thisDataMap = DataMatrix.parsing(outFile);
			Set<String> newdataKeys = dataMap.keySet();
			for (String newdataKey : newdataKeys) {
				// key存在
				if (thisDataMap.containsKey(newdataKey)) {
					thisDataMap.get(newdataKey).addAll(dataMap.get(newdataKey));
				}
				// key不存在
				else {
					thisDataMap.put(newdataKey, dataMap.get(newdataKey));
				}
			}
			// data = DataTool.ObjectToByte(thisDataMap);
			try {
				data = Json.toJson(thisDataMap).getBytes("UTF-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		// 新建
		else {
			// data = DataTool.ObjectToByte(dataMap);
			try {
				data = Json.toJson(dataMap).getBytes("UTF-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		DataMatrix.confuse(data);
		Files.write(new File(out), data);
		log.info("learning data write file success");
	}

	@Override
	public void load(String key) {
		if (!Lang.isEmpty(trainData)) {
			return;
		}
		// key 可能是键值,也可能是文件路径
		if (DataLoader.getInstance().containKey(key)) {
			// 获取训练数据
			trainData = DataLoader.getInstance().load(key);
		} else {
			// 其他
			File file = new File(key);
			if (file.exists()) {
				if (file.isFile()) {
					trainData = DataMatrix.parsing(file);
				}
				if (file.isDirectory()) {
					for (File f : file.listFiles()) {
						Map<String, Set<DataMatrix>> dataMap = DataMatrix.parsing(f);
						DataLoader.getInstance().mergeMap(trainData, dataMap);
					}
				}
			}
			// 加载失败
		}
		if (log.isDebugEnabled()) {
			if (log.isDebugEnabled()) {
				log.info("[" + key + "]chars:");
				StringBuffer buffer = new StringBuffer();
				int index = 1;
				for (String key1 : trainData.keySet()) {
					buffer = index % 100 == 0 ? buffer.append(key1) : buffer.append(key1).append(",");
					if (index % 100 == 0 || index == trainData.size()) {
						log.debug(buffer.toString());
						buffer = new StringBuffer();
					}
					index++;
				}
			}
			if (Lang.isEmpty(trainData)) {
				log.debug("load train data fail !");
			} else {
				log.debug("load train data success !");
			}
		}
	}

	@Override
	public String predict(Mat mat) {
		try {
			tester.showNewWindow("汉明距离分类器", mat);
			String result = seek(new MatMatrix(mat).getData()).iterator().next();
			return result;
		} catch (Exception e) {
			// 异常就不管了!
			e.printStackTrace();
			return "?";
		}
	}

	@Override
	public String predict(Mat mat, String... contents) {
		try {
			tester.showNewWindow("汉明距离分类器", mat);
			String result = seek(new MatMatrix(mat).getData(), contents).iterator().next();
			return result;
		} catch (Exception e) {
			// 异常就不管了!
			return "?";
		}
	}

	@Override
	public String predict(String filepath) {
		Mat mat = null;
		try {
			mat = Mats.imread(filepath);
			mat = new GrayProcess(mat).process();
			return predict(mat);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			Mats.releseMat(mat);
		}
		return null;
	}

	public List<String> predicts(Mat mat) {
		try {
			tester.showNewWindow("汉明距离分类器", mat);
			return seek(new MatMatrix(mat).getData());
		} catch (Exception e) {
			// 异常就不管了!
			e.printStackTrace();
			return new ArrayList<String>();
		}
	}

	/**
	 * 输入图像矩阵返回相似字符
	 * 
	 * @param shardData
	 * @return
	 */
	private List<String> seek(int[] shardData, String... contents) {
		int length = contents.length;
		if (Lang.isEmpty(trainData)) {
			log.warn("Training data not loaded !");
			return Lang.list("?");
		}
		List<Reuslt> results = new ArrayList<Reuslt>();
		for (String key : trainData.keySet()) {
			if (length != 0) {
				boolean flag = false;
				for (String content : contents) {
					if (content.contains(key)) {
						flag = true;
						break;
					}
				}
				if (!flag) {
					continue;
				}
			}
			Set<DataMatrix> datas = trainData.get(key);
			List<Integer> lists = new ArrayList<Integer>();

			for (DataMatrix dataMatrix : datas) {
				int[] data = dataMatrix.getData();
				// 不相同的个数
				// int different = comparator(data, shardData);
				// 相同的个数
				// int same = 30 * 30 - different;
				// int integer = same * 2 - different;
				// int integer = same - different * 2;
				lists.add(900 - comparator(data, shardData));
			}
			// 取最大值
			Collections.sort(lists, new Comparator<Integer>() {
				@Override
				public int compare(Integer o1, Integer o2) {
					if (o1 > o2)
						return -1;
					if (o1 < o2) {
						return 1;
					}
					return 0;
				}
			});
			results.add(new Reuslt(key, lists.iterator().hasNext() ? lists.iterator().next() : 0));
		}

		Collections.sort(results, new Comparator<Reuslt>() {
			@Override
			public int compare(Reuslt o1, Reuslt o2) {
				if (o1.index > o2.index) {
					return -1;
				}
				if (o1.index < o2.index) {
					return 1;
				}
				return 0;
			}
		});

		List<String> chars = new ArrayList<String>(5);
		for (Reuslt result : results) {
			chars.add(result.name);
			if (chars.size() >= 5) {
				break;
			}
		}

		if (!Lang.isEmpty(results)) {
			similarity = results.get(0).index / 900.0f;
		}
		return chars;
	}

	/**
	 * 不同的个数
	 * 
	 * @param integer1
	 * @param integer2
	 * @return
	 */
	private int comparator(int[] integer1, int[] integer2) {
		int index = 0;
		for (int i = 0; i < integer1.length && i < integer2.length; i++) {
			int result = integer1[i] ^ integer2[i];
			index += numberOf1(result);
		}
		return index;
	}

	/**
	 * 整数二进制的个数
	 * 
	 * @param n
	 * @return
	 */
	private int numberOf1(int n) {
		int count = 0;
		int flag = 1;
		while (flag != 0) {
			if ((n & flag) != 0) {
				count++;
			}
			flag <<= 1;
		}
		return count;
	}

	/**
	 * 中文字符和相似度
	 * 
	 * @author geor.lusongnan
	 */
	private class Reuslt {
		String name;
		int index;

		public Reuslt(String name, int index) {
			super();
			this.name = name;
			this.index = index;
		}

		@Override
		public String toString() {
			return name + ":" + index;
		}
	}

	float similarity = 0.0f;

	@Override
	public float getSimilarity() {
		return similarity;
	}

}
